using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Phoenix.Frames;
using Phoenix.Lifetime;

namespace Phoenix.Framework
{
    public class ControllerActionContext
    {
        /// <summary>
        /// Creates an Empty ControllerActionContext (used when there is no context available)
        /// </summary>
        /// <param name="host"></param>
        public ControllerActionContext(IPhoenixHost host)
        {
            Host = host;
        }

        public ControllerActionContext(
            ControllerAction controllerAction,
            INavigationFrame navigationFrame,
            IContentFrame contentFrame,
            IPhoenixHost host,
            IRequestTransientStore requestState,
            IEnumerable<ControllerActionParameter> parameters, 
            InvokeOptions options)
        {
            Options = options;
            var controllerActionParameters = new List<ControllerActionParameter>();
            Parameters = controllerActionParameters;
            controllerActionParameters.AddRange(parameters);
            ControllerAction = controllerAction;
            NavigationFrame = navigationFrame;
            ContentFrame = contentFrame;
            Host = host;
            RequestState = requestState;
        }

        public virtual ControllerAction ControllerAction { get; private set; }
        public virtual IEnumerable<ControllerActionParameter> Parameters { get; private set; }

        public virtual INavigationFrame NavigationFrame { get; private set; }
        public virtual IContentFrame ContentFrame { get; private set; }
        public virtual IPhoenixHost Host { get; private set; }
        public virtual IRequestTransientStore RequestState { get; private set; }
        public virtual InvokeOptions Options { get; private set; }

        /// <summary>
        /// Used to signal that a InvokeWithResult call to complete and pass back a result
        /// </summary>
        public TaskCompletionSource<object> CompletionSource { get; internal set; }

        /// <summary>
        /// Sets the result and returns back to the previous screen
        /// </summary>
        /// <param name="result"></param>
        protected void SetResult(object result)
        {
            if (CompletionSource != null)
            {
                NavigationFrame.NavigateBack();
                CompletionSource.SetResult(result);
            }
        }

        public IContentFrame ResolveFrame(string frameName)
        {
            return 
                FindFrameByName(ContentFrame, frameName) ??
                FindFrameByNameSearchingDown(ContentFrame, frameName);
        }

        static IContentFrame FindFrameByNameSearchingDown(IContentFrame contentFrame, string contentFrameName)
        {
            var child = contentFrame.ChildFrames.FirstOrDefault(f => f.Name == contentFrameName);
            if (child != null) return child;

            return contentFrame.ChildFrames.Select(s => FindFrameByNameSearchingDown(s, contentFrameName)).FirstOrDefault(c => c != null);
        }

        static IContentFrame FindFrameByName(IContentFrame contentFrame, string contentFrameName)
        {
            var child = contentFrame.ChildFrames.FirstOrDefault(f => f.Name == contentFrameName);

            if (child != null) return child;
            if (contentFrame.ParentFrame == null) return null;

            return FindFrameByName(contentFrame.ParentFrame, contentFrameName);
        }
    }
}